/*-----------------------------------------------------------------------*/
/* Low level disk I/O module SKELETON for FatFs     (C)ChaN, 2019        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control modules to the FatFs module with a defined API.       */
/*-----------------------------------------------------------------------*/
#include "stdio.h"
#include "string.h"
#include "ff.h"			/* Obtains integer types */
#include "diskio.h"		/* Declarations of disk functions */
#include "sdcard.h"
#include "DevicesDelay.h"

/* Definitions of physical drive number for each drive */
#define DEV_MMC		0	/* Example: Map MMC/SD card to physical drive 1 */
#define DEV_RAM		1	/* Example: Map Ramdisk to physical drive 0 */
#define DEV_USB		2	/* Example: Map USB MSD to physical drive 2 */


/*-----------------------------------------------------------------------*/
/* Get Drive Status                                                      */
/*-----------------------------------------------------------------------*/
/* pdrv : Physical drive nmuber to identify the drive */
DSTATUS disk_status ( BYTE pdrv )
{
    // printf("\n\rdisk_status pdrv: %d\r\n", pdrv);

    switch (pdrv)
    {
        case DEV_MMC : return RES_OK;

        default : return STA_NOINIT;
    }

    return STA_NOINIT;
}

/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
/*-----------------------------------------------------------------------*/
/* pdrv : Physical drive nmuber to identify the drive */
DSTATUS disk_initialize ( BYTE pdrv )
{
    // printf("\n\rdisk_initialize pdrv: %d\r\n", pdrv);

    switch (pdrv)
    {
        case DEV_MMC : return RES_OK;

        default : return STA_NOINIT;
    }

    return STA_NOINIT;
}

/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/
/* pdrv ： Physical drive nmuber to identify the drive */
/* buff ： Data to be written */
/* sector ： Start sector in LBA */
/* count ： Number of sectors to write */
DRESULT disk_read ( BYTE pdrv, BYTE *buff, LBA_t sector, UINT count )
{
    sd_error_enum status = SD_OK;
    static uint8_t ucBlockBuff[2048] = {0};

    // printf("\n\rdisk_read pdrv: %d, sector: %d, count: %d\r\n", pdrv, sector, count);

    switch (pdrv)
    {
        case DEV_MMC :
            if((uint32_t)buff & 3)
            {
                printf("disk_read buff is: %08X.\r\n", (int)buff);
            }
            if(count < 4)
            {
                memset(ucBlockBuff, 0, sizeof(ucBlockBuff));
                status = sd_multiblocks_read((uint32_t *)ucBlockBuff, sector * FF_MIN_SS, 512, 4);
                memcpy(buff, ucBlockBuff, count * 512);
            }
            else
            {
                status = sd_multiblocks_read((uint32_t *)buff, sector * FF_MIN_SS, 512, count * FF_MIN_SS / 512);
            }
            while(sd_transfer_state_get() != RESET);
            if(status != SD_OK)
            {
                printf("sd_multiblocks_read status: %d\r\n", status);
                printf("sd_multiblocks_read:");
                for(int i = 0; i < count * FF_MIN_SS; ++i)
                {
                    if((i % 32) == 0)
                        printf("\r\n");
                    printf("%02X ", buff[i]);
                }
                printf("\r\n");
            }

            return RES_OK;

        default : return RES_PARERR;
    }

    return RES_PARERR;
}

/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

#if FF_FS_READONLY == 0
/* pdrv ： Physical drive nmuber to identify the drive */
/* buff ： Data to be written */
/* sector ： Start sector in LBA */
/* count ： Number of sectors to write */
DRESULT disk_write ( BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count )
{
    // printf("\n\rdisk_write pdrv: %d, sector: %d, count: %d\r\n", pdrv, sector, count);

    switch (pdrv)
    {
        case DEV_MMC :
            sd_multiblocks_write((uint32_t *)buff, sector * 2048, 512, count * 4);
            while(sd_transfer_state_get() != RESET);
            return RES_OK;

        default : return RES_PARERR;
    }

    return RES_PARERR;
}

#endif

/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/
/* pdrv ： Physical drive nmuber (0..) */
/* cmd ： Control code */
/* buff ： Buffer to send/receive control data */
DRESULT disk_ioctl ( BYTE pdrv, BYTE cmd, void *buff )
{
    DRESULT res = RES_OK;
    int result = 0;

    // printf("\n\rdisk_ioctl pdrv: %d, cmd: %d\r\n", pdrv, cmd);

    switch (pdrv)
    {
        case DEV_MMC :
            switch (cmd)
            {
                /* make sure that no pending write process */
                case CTRL_SYNC: return RES_OK;

                /* get number of sectors on the disk (dword) */
                case GET_SECTOR_COUNT: *(DWORD*)buff = 1024 * 1024 * 1024 / 512; return RES_OK;

                /* get r/w sector size (word) */
                case GET_SECTOR_SIZE:  *(WORD*)buff = 512; return RES_OK;

                /* get erase block size in unit of sector (dword) */
                case GET_BLOCK_SIZE:   *(DWORD*)buff = 8; return RES_OK;

                default: return RES_PARERR;
            }

        default : return RES_PARERR;
    }

    return RES_PARERR;
}

